using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;

using UnityEditor;
using UnityEditor.Compilation;

using UnityEngine;

[InitializeOnLoad]
public class LayerAndTagEditor : AssetPostprocessor
{
    /// <summary> Tags配置文件所在路径 </summary>
    private const string TagAssetsPath = "ProjectSettings/TagManager.asset";

    /// <summary> Tags代码生成输入路径 </summary>
    private const string TagsOutPutFilePath = "/GameHelper.Generated/";

    /// <summary> Tags代码文件名称 </summary>
    private const string TagsOutPutFileName = "TagAndLayerDefine.cs";

    /// <summary> Tags基础代码 </summary>
    private const string TagsAndLayerCode = @"// This code automatically generated by LayerAndTagEditor
using System;
namespace GameHelper.Generated
{
	public static partial class TagDefines
	{	$TagsCode$
	}
	
	public static partial class LayerDefines
	{	$LayeCode$
	}
}";

    static LayerAndTagEditor()
    {
        if (!Application.isPlaying && !File.Exists(Application.dataPath + TagsOutPutFilePath + TagsOutPutFileName))
        {
            string[] tags = UnityEditorInternal.InternalEditorUtility.tags;
            string[] layers = UnityEditorInternal.InternalEditorUtility.layers;
            GenerateTagDefineFile(tags, layers);
        }
    }

    /// <summary>
    /// 资源变动回调
    /// </summary>
    /// <param name="importedAsset"></param>
    /// <param name="deletedAssets"></param>
    /// <param name="movedAssets"></param>
    /// <param name="movedFromAssetPaths"></param>
    private static void OnPostprocessAllAssets(string[] importedAsset, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths)
    {
        if (Application.isPlaying)
            return;

        bool importebTagAssetFlag = importedAsset.Any((x) => { return string.Equals(x, TagAssetsPath); });
        bool deletedTagAssetFlag = deletedAssets.Any((x) => { return string.Equals(x, TagAssetsPath); });

        if (importebTagAssetFlag || deletedTagAssetFlag)
        {
            string[] tags = UnityEditorInternal.InternalEditorUtility.tags;
            string[] layers = UnityEditorInternal.InternalEditorUtility.layers;
            GenerateTagDefineFile(tags, layers);
        }
    }

    private static string GetPlayerAssembly()
    {
        return UnityEditor.Compilation.CompilationPipeline.GetAssemblies(AssembliesType.Player)[0].name;
    }

    private static void GenerateTagDefineFile(string[] tags, string[] layers)
    {
        if (File.Exists(Application.dataPath + TagsOutPutFilePath + TagsOutPutFileName))
        {
            try
            {
                Type tagClass = System.Type.GetType("GameHelper.Generated.TagDefines," + "Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");

                List<FieldInfo> tagFieldInfos = tagClass.GetFields().ToList();//获取Tags类的所有字段
                List<string> obsoleteTags = new List<string>();//需要打丢弃特性的字段名称数组
                for (int i = 0; i < tagFieldInfos.Count; i++)
                {
                    if (tagFieldInfos[i].FieldType != typeof(string))
                        continue;

                    //查询是否存在废弃的标签
                    //TagManager.Assets中不存在某一标签但生成的脚本中存在则脚本中的标签标记为丢弃
                    bool flag = tags.Any((x) =>
                    {
                        string tagName = x.Replace(" ", "_");
                        return tagName == tagFieldInfos[i].Name;
                    });
                    if (!flag) obsoleteTags.Add(tagFieldInfos[i].Name); //此字段标记为丢弃
                }

                //以下重新生成Tag代码
                string tagContent = "";
                string tagArrayItemContent = "";
                int tagLength = tags.Length;
                for (int i = 0; i < tagLength; i++)
                {
                    tagContent += GenaratorItemTag(tags[i]);
                    tagArrayItemContent += GenaratorTagArrayItem(tags[i]);
                }

                for (int i = 0; i < obsoleteTags.Count; i++)
                    tagContent += GenaratorItemTag(obsoleteTags[i], true);

                Type LayerClass = System.Type.GetType("GameHelper.Generated.LayerDefines," + "Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null");

                List<FieldInfo> layerFieldInfos = LayerClass.GetFields().ToList();//获取Layers类的所有字段
                List<string> obsoleteLayers = new List<string>();//需要打丢弃特性的字段名称数组
                int layersLength = layers.Length;
                for (int i = 0; i < layerFieldInfos.Count; i++)
                {
                    if (layerFieldInfos[i].FieldType != typeof(string))
                        continue;

                    //查询是否存在废弃的层
                    //TagManager.Assets中不存在某一层标但生成的脚本中存在则脚本中的层标标记为丢弃
                    bool flag = layers.Any((x) =>
                    {
                        string tagName = x.Replace(" ", "_");
                        return tagName == layerFieldInfos[i].Name;
                    });
                    if (!flag) obsoleteLayers.Add(layerFieldInfos[i].Name); //此字段标记为丢弃
                }

                //以下生成Layer代码
                string layerContent = "";
                string layerArrayItemContent = "";
                int layerLength = layers.Length;
                for (int i = 0; i < layerLength; i++)
                {
                    layerContent += GenaratorItemLayer(layers[i]);
                    layerContent += GenaratorItemLayerValue(layers[i]);
                    layerArrayItemContent += GenaratorLayerArrayItem(layers[i]);
                }

                for (int i = 0; i < obsoleteLayers.Count; i++)
                {
                    layerContent += GenaratorItemLayer(obsoleteLayers[i], true);
                    layerContent += GenaratorItemLayerValue(obsoleteLayers[i], true);
                }

                //写入文件
                string writes = TagsAndLayerCode;
                writes = writes.Replace("$TagsCode$", (tagContent + GenaratorTagArrayContent(tagArrayItemContent)));
                writes = writes.Replace("$LayeCode$", (layerContent + GenaratorLayerArrayContent(layerArrayItemContent)));
                //修改的内容覆盖原文本
                StreamWriter sw = new StreamWriter(Application.dataPath + TagsOutPutFilePath + TagsOutPutFileName, false);
                sw.Write(writes); sw.Close();
            }
            catch (Exception ex)
            {
                //如有重名文件是否将与默认路径不同的脚本删除 - todo
                File.Delete(Application.dataPath + TagsOutPutFilePath + TagsOutPutFileName);
                CreatCodeFile(tags, layers);
                Debug.LogError(ex);
            }
        }
        else
        {
            CreatCodeFile(tags, layers);
        }
    }

    /// <summary>
    /// 创建脚本文件
    /// </summary>
    /// <param name="tags"></param>
    /// <param name="layers"></param>
    private static void CreatCodeFile(string[] tags, string[] layers)
    {
        //判断文件夹路径是否存在
        if (!Directory.Exists(Application.dataPath + TagsOutPutFilePath))
            Directory.CreateDirectory(Application.dataPath + TagsOutPutFilePath);

        string writes = TagsAndLayerCode;
        writes = writes.Replace("$TagsCode$", GenaratorTagDefine(tags));
        writes = writes.Replace("$LayeCode$", GenaratorLayerDefine(layers));
        File.WriteAllBytes(Application.dataPath + TagsOutPutFilePath + TagsOutPutFileName, Encoding.UTF8.GetBytes(writes));
    }

    #region GenaratorTag

    /// <summary>
    /// 生成标签所有内容
    /// </summary>
    /// <param name="tags"></param>
    /// <returns></returns>
    private static string GenaratorTagDefine(string[] tags)
    {
        string content = "";
        string allTagsDes = "";
        int length = tags.Length;
        for (int i = 0; i < length; i++)
        {
            //if (isHasTag(tagName)) continue;
            content += GenaratorItemTag(tags[i]);
            allTagsDes += GenaratorTagArrayItem(tags[i]);
        }
        return content + GenaratorTagArrayContent(allTagsDes);
    }

    /// <summary>
    /// 生成标签数组内容
    /// </summary>
    /// <param name="tags"></param>
    /// <returns></returns>
    private static string GenaratorTagArrayContent(string tags)
    {
        return "\r\r\n\t\tpublic static readonly string[] AllTags = {\r\n\t\t" + tags + "\r\n\t\t};";
    }

    /// <summary>
    /// 生成指定标签定义
    /// </summary>
    /// <param name="tag"></param>
    /// <param name="isObsoleteFlag"></param>
    /// <returns></returns>
    private static string GenaratorItemTag(string tag, bool isObsoleteFlag = false)
    {
        if (!isObsoleteFlag)
            return String.Format("\r\n\t\tpublic const string {0} = \"{1}\";", tag.Replace(" ", "_"), tag);
        else
            return String.Format("\r\n\t\t[Obsolete]public const string {0} = \"{1}\";", tag.Replace(" ", "_"), tag);
    }

    /// <summary>
    /// 生成数组内的标签内容
    /// </summary>
    /// <param name="tag"></param>
    /// <returns></returns>
    private static string GenaratorTagArrayItem(string tag)
    {
        return String.Format("\r\n\t\t\t\"{0}\",", tag);
    }

    /// <summary>
    /// 查找是否含有某个指定标签
    /// </summary>
    /// <param name="tag"></param>
    /// <returns></returns>
    private static bool isHasTag(string tag)
    {
        for (int i = 0; i < UnityEditorInternal.InternalEditorUtility.tags.Length; i++)
        {
            if (UnityEditorInternal.InternalEditorUtility.tags[i].Contains(tag))
                return true;
        }
        return false;
    }

    #endregion

    #region GenaratorLayer

    /// <summary>
    /// 生成层标所有内容
    /// </summary>
    /// <param name="layers"></param>
    /// <returns></returns>
    private static string GenaratorLayerDefine(string[] layers)
    {
        string content = "";
        string allLayersDes = "";
        int length = layers.Length;
        for (int i = 0; i < length; i++)
        {
            //if (isHasLayer(layerName)) continue;
            content += GenaratorItemLayer(layers[i]);
            content += GenaratorItemLayerValue(layers[i]);
            allLayersDes += GenaratorLayerArrayItem(layers[i]);
        }
        return content + GenaratorLayerArrayContent(allLayersDes);
    }

    /// <summary>
    /// 生成层标数组内容
    /// </summary>
    /// <param name="layers"></param>
    /// <returns></returns>
    private static string GenaratorLayerArrayContent(string layers)
    {
        return "\r\r\n\t\tpublic static readonly string[] AllLayers = {\r\n\t\t" + layers + "\r\n\t\t};";
    }

    /// <summary>
    /// 生成指定层标定义
    /// </summary>
    /// <param name="layer"></param>
    /// <param name="isObsoleteFlag"></param>
    /// <returns></returns>
    private static string GenaratorItemLayer(string layer, bool isObsoleteFlag = false)
    {
        if (!isObsoleteFlag)
            return String.Format("\r\n\t\tpublic const string {0} = \"{1}\";", layer.Replace(" ", "_"), layer);
        else
            return String.Format("\r\n\t\t[Obsolete]public const string {0} = \"{1}\";", layer.Replace(" ", "_"), layer);
    }

    /// <summary>
    /// 生成指定层标定义
    /// Temp
    /// </summary>
    /// <param name="layer"></param>
    /// <param name="isObsoleteFlag"></param>
    /// <returns></returns>
    private static string GenaratorItemLayerValue(string layer, bool isObsoleteFlag = false)
    {
        LayerMask layerMask = LayerMask.NameToLayer(layer);
        if (!isObsoleteFlag)
            return String.Format("\r\n\t\tpublic const int {0} = {1};", layer.Replace(" ", "_") + "Value", layerMask.value);
        else
            return String.Format("\r\n\t\t[Obsolete]public const int {0} = {1};", layer.Replace(" ", "_") + "Value", layerMask.value);
    }

    /// <summary>
    /// 生成数组内的层标内容
    /// </summary>
    /// <param name="layer"></param>
    /// <returns></returns>
    private static string GenaratorLayerArrayItem(string layer)
    {
        return String.Format("\r\n\t\t\t\"{0}\",", layer);
    }

    /// <summary>
    /// 查找指定层标是否存在
    /// </summary>
    /// <param name="layer"></param>
    /// <returns></returns>
    private static bool isHasLayer(string layer)
    {
        for (int i = 0; i < UnityEditorInternal.InternalEditorUtility.layers.Length; i++)
        {
            if (UnityEditorInternal.InternalEditorUtility.layers[i].Contains(layer))
                return true;
        }
        return false;
    }

    #endregion
}
